#include <cstdlib>
#include <netdb.h>
#include <sstream>
#include <signal.h>
#include <sys/time.h>
#include <errno.h>
#include <cstring>

#include "ui_socket.h"
#include "ui_constants.h"
#include "logger.h"

#define MAX_BYTES 16

using namespace std;

pthread_mutex_t CUISocket::sSync = PTHREAD_MUTEX_INITIALIZER;

pthread_cond_t CUISocket::sConnectSignal = PTHREAD_COND_INITIALIZER;
pthread_cond_t CUISocket::sStartSignal = PTHREAD_COND_INITIALIZER;

CUISocket* CUISocket::sInstance = NULL;

const std::string CUISocket::sIniSection = "USER_INTERFACE";
const uint32_t CUISocket::sMaxDataLength = 1048576; /* 1 MB */

CUISocket* CUISocket::getInstance()
{
	while(pthread_mutex_lock(&sSync)) {
		printl_console(LL_ERR, __FILE__, "Fatal error: Can't lock mutex! try again.\n");
	}

	if(!sInstance) {
		// create the first and only object (or a destroy call was between)
		sInstance = new CUISocket();
	}
	if(pthread_mutex_unlock(&sSync)) {
		printl_console(LL_ERR, __FILE__, "Fatal error: Can't unlock mutex!\n");
	}
	return sInstance;
}

void CUISocket::destroy()
{
	while(pthread_mutex_lock(&sSync)) {
		printl_console(LL_ERR, __FILE__, "Fatal error: Can't lock mutex! try again.\n");
	}
	if(sInstance) {
		delete sInstance;
		sInstance = NULL;
	}
	if(pthread_mutex_unlock(&sSync)) {
		printl_console(LL_ERR, __FILE__, "Fatal error: Can't unlock mutex!\n");
	}
}

CUISocket::CUISocket() :
		mConfig(NULL),
		mAmpMulti(0.0),
		mServerSocket(-1),
		mClientSocket(-1),
		mAllowClients(true),
		mWaitTime(0),
		mStartSignalValue(false),
		mRunning(NULL),
		mShutdown(false),
		mProtocolDataLength(0),
		mProtocolDataNextIndex(0),
		mProtocolData(NULL)
{
	pthread_mutex_init(&mCommandSendSync, NULL);
	pthread_mutex_init(&mSendSync, NULL);
	pthread_mutex_init(&mProtocolSync, NULL);
	pthread_mutex_init(&mAmpSync, NULL);

	printl_console(0, __FILE__, "Create new User Interface Socket.\n");
}

CUISocket::~CUISocket()
{
	mAllowClients = false;
	if(mServerSocket != -1) {
		printl_console(0, __FILE__, "Send close command.\n");
		sendCommand(COMMAND_CLOSE_CONNECTION, NULL, 0);
		sleep(1); // give client time to close connection
		printl_console(0, __FILE__, "Wait for server thread.\n");
		mShutdown = true;
		pthread_join(mServerThdId, NULL);
		printl_console(0, __FILE__, "Closing server socket.\n");
		close(mServerSocket);
	}
	if(mClientSocket != -1) {
		// should not be possible
		printl_console(LL_WARN, __FILE__, "Closing client socket. (But should already be closed)\n");
		close(mClientSocket);
	}
	pthread_mutex_destroy(&mAmpSync);
	pthread_mutex_destroy(&mProtocolSync);
	pthread_mutex_destroy(&mSendSync);
	pthread_mutex_destroy(&mCommandSendSync);

	printl_console(0, __FILE__, "Delete User Interface Socket.\n");
}

bool CUISocket::setPropertiesReader(CPropertiesReader* pConfig)
{
	if(pthread_mutex_lock(&sSync)) {
		return false;
	}
	if(mConfig) {
		// properties reader already set. no change allowed. otherwise sync problems
		pthread_mutex_unlock(&sSync);
		return false;
	}
	mConfig = pConfig;
	pthread_mutex_unlock(&sSync);
	return true;
}

/**
 * create a new server thread for user interface socket.
 * Name/IP and Port are used from config.
 */
bool CUISocket::startUIServer()
{
	if(pthread_mutex_lock(&sSync)) {
		return false;
	}
	if(mServerSocket != -1) {
		printl_console(0, __FILE__, "Server already started.\n");
		pthread_mutex_unlock(&sSync);
		return false;
	}
	if(!mConfig) {
		printl_console(0, __FILE__, "No config for User Interface available.\n");
		pthread_mutex_unlock(&sSync);
		return false;
	}
	string lStrValue = "";
	if(!mConfig->containsKey(sIniSection, "ACTIVATE")
	|| !mConfig->getValue(sIniSection, "ACTIVATE", lStrValue)) {
		printl_console(LL_ERR, __FILE__, "Config error: ACTIVATE [%s]\n", sIniSection.c_str());
		pthread_mutex_unlock(&sSync);
		return false;
	}
	if(lStrValue != "true"
	&& lStrValue != "True"
	&& lStrValue != "TRUE") {
		// != true -> user interface is disabled
		printl_console(0, __FILE__, "User interface is disabled.\n");
		pthread_mutex_unlock(&sSync);
		return true;
	}

	if(!mConfig->containsKey(sIniSection, "NAME")
	|| !mConfig->getValue(sIniSection, "NAME", lStrValue)) {
		printl_console(LL_ERR, __FILE__, "Config error: NAME [%s]\n", sIniSection.c_str());
		pthread_mutex_unlock(&sSync);
		return false;
	}
	struct hostent* lHostEntry = gethostbyname(lStrValue.c_str());
	if(!lHostEntry) {
		printl_console(LL_ERR, __FILE__, "Can't resolve %s\n", lStrValue.c_str());
		pthread_mutex_unlock(&sSync);
		return false;
	}
	printl_console(0, __FILE__, "Name: %s\n", lHostEntry->h_name);
	for(unsigned int i = 0; lHostEntry->h_addr_list[i]; i++) {
		printl_console(0, __FILE__, "IP: %s\n", inet_ntoa( *(struct in_addr*)(lHostEntry->h_addr_list[i])));
	}

	if(!mConfig->containsKey(sIniSection, "PORT")
	|| !mConfig->getValue(sIniSection, "PORT", lStrValue)) {
		printl_console(LL_ERR, __FILE__, "Config error: PORT [%s]\n", sIniSection.c_str());
		pthread_mutex_unlock(&sSync);
		return false;
	}
	uint16_t lPort(0);
	{
		stringstream ss;
		ss << lStrValue;
		ss >> lPort;
	}
	printl_console(0, __FILE__, "Port: %u\n", lPort);

	if(!mConfig->containsKey(sIniSection, "WAIT_TIME")
	|| !mConfig->getValue(sIniSection, "WAIT_TIME", lStrValue)) {
		printl_console(LL_ERR, __FILE__, "Config error: WAIT_TIME [%s]\n", sIniSection.c_str());
		pthread_mutex_unlock(&sSync);
		return false;
	}
	{
		stringstream ss;
		ss << lStrValue;
		ss >> mWaitTime;
	}
	printl_console(0, __FILE__, "Wait time: %u\n", mWaitTime);

	printl_console(0, __FILE__, "Start server for CUISocket\n");
	mServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(mServerSocket == -1) {
		printl_console(LL_ERR, __FILE__, "Socket error\n");
		pthread_mutex_unlock(&sSync);
		return false;
	}

	mServerAddress.sin_family = AF_INET;
	mServerAddress.sin_addr.s_addr = *(in_addr_t*)(lHostEntry->h_addr_list[0]);
	mServerAddress.sin_port = htons(lPort);

	if(bind(mServerSocket, (struct sockaddr *)&mServerAddress, sizeof(struct sockaddr_in)) == -1) {
		printl_console(LL_ERR, __FILE__, "Binding error\n");
		close(mServerSocket);
		mServerSocket = -1;
		pthread_mutex_unlock(&sSync);
		return false;
	}
	if(listen(mServerSocket, 1) == -1) {
		printl_console(LL_ERR, __FILE__, "Listing error\n");
		close(mServerSocket);
		mServerSocket = -1;
		pthread_mutex_unlock(&sSync);
		return false;
	}

	if(pthread_create(&mServerThdId, NULL, pthreadForServerAccept, this)) {
		printl_console(LL_ERR, __FILE__, "Create server thread failed.\n");
		close(mServerSocket);
		mServerSocket = -1;
	}
	pthread_mutex_unlock(&sSync);
	return true;
}

bool CUISocket::waitForUIClientAndStartSignal()
{
	pthread_mutex_lock(&sSync);
	if(mServerSocket == -1) {
		// no server socket -> no wait
		pthread_mutex_unlock(&sSync);
		return true;
	}

	if(mClientSocket == -1) {
		struct timespec lTS;
		struct timeval lTP;
		gettimeofday(&lTP, NULL);
		lTS.tv_sec  = lTP.tv_sec;
		lTS.tv_nsec = lTP.tv_usec * 1000;
		lTS.tv_sec += mWaitTime;

		int rv = pthread_cond_timedwait(&sConnectSignal, &sSync, &lTS);
		if(rv) {
			if(ETIMEDOUT) {
				printl_console(0, __FILE__, "Timeout for connection.\n");
			}
			pthread_mutex_unlock(&sSync);
			return true;
		}
	}

	/* check if server process has been requested to shut down */
	if (!(*mRunning))
	{
		pthread_mutex_unlock(&sSync);
		return true;
	}

	// client connection exist -> wait for start signal
	// FIXME once here the core cannot be shut down by a SIGINT signal
	int rv = pthread_cond_wait(&sStartSignal, &sSync);
	if(rv) {
		printl_console(LL_ERR, __FILE__, "Start signal error.");
		pthread_mutex_unlock(&sSync);
		return false;
	}

	bool lSignal = mStartSignalValue;
	pthread_mutex_unlock(&sSync);
	return lSignal;
}

void CUISocket::setAmpMultiplier(double pAmp)
{
	pthread_mutex_lock(&mAmpSync);
	mAmpMulti = pAmp;
	pthread_mutex_unlock(&mAmpSync);
}

double CUISocket::getAmpMultiplier()
{
	pthread_mutex_lock(&mAmpSync);
	double lAmp = mAmpMulti;
	pthread_mutex_unlock(&mAmpSync);
	return lAmp;
}

/*------------------ start of send command methodes ------------------*/

bool CUISocket::sendInfo(uint8_t pLogLevel,
		const char* pMeta, int pMetaLen,
		const char* pInfo, int pInfoLen)
{
	pthread_mutex_lock(&mCommandSendSync);
	uint8_t lCommand(COMMAND_TRANSMIT_INFO);

	/* first send the complete length */
	if(!sendLength(sizeof(lCommand) + sizeof(pLogLevel) + pMetaLen + pInfoLen))
	{
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}
	/* send the command itself */
	if (sendData(&lCommand, sizeof(lCommand)) != sizeof(lCommand))
	{
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}
	/* send the log-legel */
	if (sendData(reinterpret_cast<const void*>(&pLogLevel), sizeof(pLogLevel)) != sizeof(pLogLevel))
	{
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	/* send the date */
	if (pMeta != NULL && pMetaLen > 0 && (sendData(pMeta, pMetaLen) != pMetaLen))
	{
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	/* then send the actual information */
	if (pInfo != NULL && pInfoLen > 0 && (sendData(pInfo, pInfoLen) != pInfoLen))
	{
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	pthread_mutex_unlock(&mCommandSendSync);
	return true; /* success */
}

void CUISocket::sendExtractBufferInfo(uint32_t pUsed, uint32_t pSize)
{
	unsigned char lPayload[8];

	*reinterpret_cast<uint32_t*>(lPayload) = htonl(pUsed);
	*reinterpret_cast<uint32_t*>(lPayload + 4) = htonl(pSize);

	sendCommand(COMMAND_EXTRACT_BUFFER_INFO, lPayload, 8);
}

void CUISocket::sendFrameSendLength(uint32_t pLength)
{
	pLength = htonl(pLength);
	sendCommand(COMMAND_FRAME_SEND_LENGTH, &pLength, 4);
}

void CUISocket::sendFrameRecvLength(uint32_t pLength, bool pError)
{
	unsigned char lPayload[5];

	*reinterpret_cast<uint32_t*>(lPayload) = htonl(pLength);
	lPayload[4] = pError ? 0xFF : 0x00;

	sendCommand(COMMAND_FRAME_RECV_LENGTH, lPayload, 5);
}

void CUISocket::sendPacketSendLength(uint32_t pLength)
{
	pLength = htonl(pLength);
	sendCommand(COMMAND_PACKET_SEND_LENGTH, &pLength, 4);
}

void CUISocket::sendPacketRecvLength(uint32_t pLength, bool pCrcError)
{
	unsigned char lPayload[5];

	*reinterpret_cast<uint32_t*>(lPayload) = htonl(pLength);
	lPayload[4] = pCrcError ? 0xFF : 0x00;

	sendCommand(COMMAND_PACKET_RECV_LENGTH, lPayload, 5);
}

void CUISocket::sendStreamSendInfo(int pId, int pAck, uint32_t pLength, bool pReplay)
{
	unsigned char lPayload[7];

	lPayload[0] = pId & 0x0F;
	lPayload[1] = pAck & 0x0F;
	*reinterpret_cast<uint32_t*>(lPayload + 2) = htonl(pLength);
	lPayload[6] = pReplay ? 0xFF : 0x00;

	sendCommand(COMMAND_STREAM_SEND_INFO, lPayload, 7);
}

void CUISocket::sendStreamSendData(void* pData, int pLength)
{
	sendCommand(COMMAND_STREAM_SEND_DATA, pData, pLength);
}

void CUISocket::sendStreamRecvInfo(int pId, int pAck, uint32_t pLength, bool pReplay)
{
	unsigned char lPayload[7];

	lPayload[0] = pId & 0x0F;
	lPayload[1] = pAck & 0x0F;
	*reinterpret_cast<uint32_t*>(lPayload + 2) = htonl(pLength);
	lPayload[6] = pReplay ? 0xFF : 0x00;

	sendCommand(COMMAND_STREAM_RECV_INFO, lPayload, 7);
}

void CUISocket::sendStreamRecvData(void* pData, int pLength)
{
	sendCommand(COMMAND_STREAM_RECV_DATA, pData, pLength);
}

bool CUISocket::sendProtocolOpen()
{
	return sendCommand(COMMAND_PROTOCOL_OPEN, NULL, 0);
}

void CUISocket::sendProtocolWrite(void* pData, int pLength)
{
	sendCommand(COMMAND_PROTOCOL_WRITE_DATA, pData, pLength);
}

void CUISocket::sendProtocolClose()
{
	sendCommand(COMMAND_PROTOCOL_CLOSE, NULL, 0);
}

void CUISocket::sendProtocolReadFeedback(uint32_t pRead, uint32_t pSize)
{
	unsigned char lPayload[8];

	*reinterpret_cast<uint32_t*>(lPayload) = htonl(pRead);
	*reinterpret_cast<uint32_t*>(lPayload + 4) = htonl(pSize);

	sendCommand(COMMAND_PROTOCOL_READ_FEEDBACK, lPayload, 8);
}

void CUISocket::sendAmpEmbed(unsigned char pAmp)
{
	sendCommand(COMMAND_AMP_EMBED, &pAmp, 1);
}

void CUISocket::sendAmpExtract(unsigned char pAmp)
{
	sendCommand(COMMAND_AMP_EXTRACT, &pAmp, 1);
}

/*------------------- end of send command methodes -------------------*/

/**
 * @return False for no data, True for data
 */
bool CUISocket::getNextProtocolReadByte(unsigned char &pReadValue)
{
	bool rv(false);
	pthread_mutex_lock(&mProtocolSync);
	if (mProtocolData &&
		mProtocolDataLength > 0 &&
		mProtocolDataNextIndex < mProtocolDataLength)
	{
		rv = true;
		pReadValue = mProtocolData[mProtocolDataNextIndex];
		mProtocolDataNextIndex++;

		// send feedback to GUI
		sendProtocolReadFeedback(mProtocolDataNextIndex, mProtocolDataLength);

		if (mProtocolDataNextIndex >= mProtocolDataLength) {
			delete [] mProtocolData;
			mProtocolData = NULL;
			mProtocolDataLength = 0;
			mProtocolDataNextIndex = 0;
			printl_console(0, __FILE__, "Ready for new data from GUI.");
		}
	}
	pthread_mutex_unlock(&mProtocolSync);
	return rv;
}


void* CUISocket::pthreadForServerAccept(void* pUISocket)
{
	return static_cast<CUISocket*>(pUISocket)->threadForServerAccept();
}

void* CUISocket::threadForServerAccept()
{
	// lock and unlock -> pthread_create finish storing thread id
	pthread_mutex_lock(&sSync);
	pthread_mutex_unlock(&sSync);
	fd_set lFd;
	struct timeval lTv;

	while (mAllowClients) {
		printl_console(0, __FILE__, "Wait for next client ...\n");
		socklen_t lLength = sizeof(struct sockaddr_in);
		while (mClientSocket == -1 && (*mRunning))
		{
			FD_ZERO(&lFd);
			FD_SET(mServerSocket, &lFd);
			lTv.tv_sec = 0;
			lTv.tv_usec = 100000;
			int lReturnSelect = select(mServerSocket + 1, &lFd, NULL, NULL, &lTv);
			if (lReturnSelect && FD_ISSET(mServerSocket, &lFd))
			{
				mClientSocket = accept(mServerSocket,
						reinterpret_cast<struct sockaddr *>(&mClientAddress),
						&lLength);
				if(mClientSocket == -1) {
					printl_console(LL_ERR, __FILE__, "Accept failed or canceled \n");
					return NULL;
				}
			}
			else /* timeout */
			{
				if (!(*mRunning) || mShutdown)
				{
					break;
				}
			}
		}

		if (!(*mRunning) || mShutdown)
		{
			pthread_cond_signal(&sConnectSignal);
			return NULL;
		}

		printl_console(LL_DEBUG, __FILE__, "Client connect from: %u.%u.%u.%u:%d\n",
				*(reinterpret_cast<unsigned char*>(&mClientAddress.sin_addr.s_addr) + 0),
				*(reinterpret_cast<unsigned char*>(&mClientAddress.sin_addr.s_addr) + 1),
				*(reinterpret_cast<unsigned char*>(&mClientAddress.sin_addr.s_addr) + 2),
				*(reinterpret_cast<unsigned char*>(&mClientAddress.sin_addr.s_addr) + 3),
				/* inet_ntoa(mClientAddress.sin_addr), */
				ntohs(mClientAddress.sin_port));

		pthread_mutex_lock(&sSync);
		pthread_cond_signal(&sConnectSignal);
		pthread_mutex_unlock(&sSync);

		clientFunction();

		if(mAllowClients) {
			pthread_mutex_lock(&sSync);
			mStartSignalValue = false;
			pthread_cond_signal(&sStartSignal);
			pthread_mutex_unlock(&sSync);
		}

		close(mClientSocket);
		mClientSocket = -1;
		printl_console(0, __FILE__, "Client disconnect.\n");
	}
	return NULL;
}

void CUISocket::clientFunction()
{
	uint32_t lLength;
	std::string lCmdData("");
	std::string lSection("");
	std::string lKey("");
	std::string lValue("");
	while(fullRecv(&lLength, 4) == 4) {
#ifdef __LITTLE_ENDIAN
		/* FIXME convert manually */
		lLength = ntohl(lLength);
#else
#endif
		if(lLength > 0 && lLength < sMaxDataLength) {
			unsigned char* lData = new unsigned char [lLength];
			if(fullRecv(lData, lLength) != (int)lLength) {
				printl_console(0, __FILE__, "no full read.\n");
				delete [] lData;
				return;
			}
			switch(lData[0]) {
				case COMMAND_START:
					pthread_mutex_lock(&sSync);
					mStartSignalValue = true;
					pthread_cond_signal(&sStartSignal);
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_TRANSMIT_CONFIG:
					pthread_mutex_lock(&sSync);
					sendConfiguration();
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_PROTOCOL_READ_DATA:
					pthread_mutex_lock(&mProtocolSync);
					if (!mProtocolData &&
						!mProtocolDataLength &&
						!mProtocolDataNextIndex)
					{
						if (lLength < 1) {
							printl_console(LL_WARN, __FILE__, "No payload.");
							pthread_mutex_unlock(&mProtocolSync);
							break;
						}
						mProtocolDataLength = lLength - 1;
						mProtocolData = new unsigned char [mProtocolDataLength];
						for (unsigned int i = 0; i < mProtocolDataLength; i++) {
							mProtocolData[i] = lData [i + 1];
						}
					}
					pthread_mutex_unlock(&mProtocolSync);
					break;
				case COMMAND_RECEIVE_CONFIG_START:
					pthread_mutex_lock(&sSync);
					printl_console(LL_DEBUG, __FILE__, "Receiving configuration start. \n");
					mConfig->dropConfig();
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_RECEIVE_CONFIG_END:
					pthread_mutex_lock(&sSync);
					mConfig->setLoaded(true);
					mConfig->write();
					printl_console(LL_DEBUG, __FILE__, "Receiving configuration end. \n");
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_RECEIVE_CONFIG_SECTION:
					pthread_mutex_lock(&sSync);
					lSection = std::string(reinterpret_cast<char*>(lData + 1));
#if 0
					printl_console(LL_DEBUG, __FILE__, "Section received: %s\n", lSection.c_str());
#endif
					mConfig->createSection(lSection);
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_RECEIVE_CONFIG_KEY:
					pthread_mutex_lock(&sSync);
					lCmdData = std::string(reinterpret_cast<char*>(lData + 1));
					lSection = lCmdData.substr(0,lCmdData.find(":"));
					lKey = lCmdData.substr(lCmdData.find(":") + 1, lCmdData.find("=") - lCmdData.find(":") - 1);
					lValue = lCmdData.substr(lCmdData.find("=") + 1);
#if 0
					printl_console(LL_DEBUG, __FILE__, "Key received: %s:%s=%s\n", 
							lSection.c_str(),
							lKey.c_str(),
							lValue.c_str());
#endif
					if (mConfig->createKey(lSection, lKey, lValue) == false)
					{
						printl_console(LL_DEBUG, __FILE__, "Key exists!\n");
					}
					pthread_mutex_unlock(&sSync);
					break;
				case COMMAND_NOISE_RECV_AMP:
				{
					if (lLength != 5) {
						printl_console(LL_ERR, __FILE__, "wrong length for recv amp cmd");
						break;
					}
					uint32_t lAmp = ntohl(*reinterpret_cast<uint32_t*>(lData + 1));
					setAmpMultiplier(static_cast<double>(lAmp) / 1000.0);
					printl_console(0, __FILE__, "noise amp changed to %f", getAmpMultiplier());
					break;
				}
				default:
					printl_console(LL_WARN, __FILE__, "Unknown UI command: %u.\n", lData[0]);
					break;
			}
			delete [] lData;
#if 0
			printl_console(0, __FILE__, "Read one packet with length %u.\n", lLength);
#endif
		}
		else
		{
			printl_console(0, __FILE__, "Illegal packet length: %u. \n", lLength);
		}
	}
	printl_console(0, __FILE__, "Read failed -> Close client function.");
}

int CUISocket::fullRecv(void *pBuf, int pLength)
{
	fd_set lFd;
	struct timeval lTv;
	int lBytesRead(0);

	if(mClientSocket == -1) {
		return -1;
	}
	if(pLength == 0) {
		// can't receive 0 bytes
		return 0;
	}

	while(lBytesRead < pLength)
	{
		FD_ZERO(&lFd);
		FD_SET(mClientSocket, &lFd);
		lTv.tv_sec = 0;
		lTv.tv_usec = 999999;
		int lReturnSelect = select(mClientSocket + 1, &lFd, NULL, NULL, &lTv);
		if (lReturnSelect && FD_ISSET(mClientSocket, &lFd))
		{
			int lReturnRecv =	recv(mClientSocket, reinterpret_cast<uint8_t*>(pBuf) + lBytesRead, pLength - lBytesRead, MSG_DONTWAIT);
			if (lReturnRecv <= 0)
			{
				return -1;
			}
			lBytesRead += lReturnRecv;
		}
		else if (lReturnSelect < 0)
		{
			/* error */
			return -1;
		}
		else /* (lReturnSelect == 0) */
		{
			/* timeout */
			if (mShutdown)
			{
				return -1;
			}
		}
	}

	return lBytesRead;
}

int CUISocket::fullSend(const void *pBuf, int pLength)
{
	if (!pBuf) {
		return -1;
	}

	if(pLength <= 0) {
		// no send is necessary
		return 0;
	}

	pthread_mutex_lock(&mSendSync);

	if(mClientSocket == -1) {
		pthread_mutex_unlock(&mSendSync);
		return -1;
	}
	//static unsigned int lOffset(0); // only for debug

	int rv(send(mClientSocket, pBuf, pLength, MSG_NOSIGNAL));
	if (rv != pLength) {
		printl_console(LL_ERR, __FILE__, "sent less bytes than available");
	}
	//printl_console(0, __FILE__, printData(lOffset, pBuf, pLength).c_str()); // only for debug
	//lOffset += pLength; // only for debug

	pthread_mutex_unlock(&mSendSync);
	return rv;
}

int CUISocket::sendLength(uint32_t pLen)
{
	pLen = htonl(pLen);
	return fullSend(&pLen, 4);
}

int CUISocket::sendData(const void* pPayload, int pLen)
{
	if (!pPayload) {
		return -1;
	}
	return fullSend(pPayload, pLen);
}

bool CUISocket::sendCommand(uint8_t pCommand, const void* pPayload, int pLen)
{
	if (!pPayload && pLen != 0) {
		printl_console(LL_ERR, __FILE__, "No payload but length != 0.");
		return false;
	}
	if (pPayload && pLen <= 0) {
		printl_console(LL_ERR, __FILE__, "Payload must be > 0 byte.");
		return false;
	}

	pthread_mutex_lock(&mCommandSendSync);
	if(mClientSocket == -1) {
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	/* first send 4 bytes (length field) = the complete length (command + payload) */
	if(sendLength(sizeof(pCommand) + pLen) != 4) 
	{
		printl_console(LL_ERR, __FILE__, "Can't send length field.");
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	/* then send the command itself (= 1 byte) */
	if (sendData(&pCommand, sizeof(pCommand)) != sizeof(pCommand))
	{
		printl_console(LL_ERR, __FILE__, "Can't send command byte.");
		pthread_mutex_unlock(&mCommandSendSync);
		return false;
	}

	/* then send the payload of the command (optional) */
	if (pLen > 0)
	{
		if(sendData(pPayload, pLen) != pLen) {
			printl_console(LL_ERR, __FILE__, "Can't send Payload");
			pthread_mutex_unlock(&mCommandSendSync);
			return false;
		}
	}
	pthread_mutex_unlock(&mCommandSendSync);
	return true;
}

bool CUISocket::sendConfiguration()
{
	TSectionIterator lIteratorSections;
	TSectionIterator lIteratorSectionsEnd;
	TKeyIterator lIteratorVariables;
	TKeyIterator lIteratorVariablesEnd;

	char lConfString[128];

	if (!(mConfig->getSectionIteratorBegin(lIteratorSections) &&
					mConfig->getSectionIteratorEnd(lIteratorSectionsEnd)))
	{
		return false;
	}

	for (;lIteratorSections != lIteratorSectionsEnd; lIteratorSections++)
	{
		if (!(mConfig->getKeyIteratorBegin(lIteratorSections->first, lIteratorVariables) &&
					mConfig->getKeyIteratorEnd(lIteratorSections->first, lIteratorVariablesEnd)))
		{
			return false;
		}
		for (;lIteratorVariables != lIteratorVariablesEnd; lIteratorVariables++)
		{
			//TODO: FIXME: why not this? -> no 128 limit
#if 0
			std::string lConfStr = lIteratorSections->first;
			lConfStr += ":";
			lConfStr += lIteratorVariables->first;
			lConfStr += "=";
			lConfStr += lIteratorVariables->second;
			sendCommand(COMMAND_TRANSMIT_CONFIG, lConfStr.c_str(), lConfStr.length());
#else
			snprintf(lConfString, 128, "%s:%s=%s", 
					lIteratorSections->first.c_str(), 
					lIteratorVariables->first.c_str(), 
					lIteratorVariables->second.c_str());
			sendCommand(COMMAND_TRANSMIT_CONFIG, lConfString, strlen(lConfString));
#endif
		}
	}
	sendCommand(COMMAND_TRANSMIT_CONFIG_END, 0, 0);

	return true;
}

std::string CUISocket::printData(unsigned int pOffset, const void *pData, int pLen)
{
	int cur_len = pLen;
	unsigned char ascii[MAX_BYTES + 1];
	ascii[MAX_BYTES] = 0;

	std::string lOutput("");
	char szOut[1000];
	sprintf(szOut, "Offset: %u, 0x%08X\n", pOffset, pOffset);
	lOutput += szOut;

	const unsigned char* lData(static_cast<const unsigned char*>(pData));
	while(pLen > 0) {
		int i;
		if(cur_len > MAX_BYTES) {
			cur_len = MAX_BYTES;
		}
		memcpy(ascii, lData, cur_len);
		ascii[cur_len] = 0;
		for(i = 0; i < cur_len; i++) {
			sprintf(szOut, "%02X ", lData[i]);
			lOutput += szOut;
			if(ascii[i] < 0x20 || ascii[i] >= 0x7F)
				ascii[i] = '.';
		}
		if(cur_len < MAX_BYTES) {
			for(i = 0; i < MAX_BYTES - cur_len; i++) {
				lOutput += "   ";
			}
		}
		lOutput += reinterpret_cast<char*>(ascii);
		lOutput += "\n";

		pLen -= MAX_BYTES;
		cur_len = pLen;
		lData += MAX_BYTES;
	}
	return lOutput;
}
